"""增强版HTTP客户端工具"""

import time
import json
import logging
from typing import Dict, Any, Optional, Union, Callable, List
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from .exceptions import (
    RequestError,
    AuthenticationError,
    RateLimitError,
    ValidationError,
    APIError
)

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class HttpClient:
    """
    增强版HTTP客户端
    提供请求拦截、响应拦截、自动重试、超时控制等高级功能
    """
    
    def __init__(self,
                 base_url: str = "",
                 timeout: int = 30,
                 max_retries: int = 3,
                 retry_delay: int = 1,
                 retry_status_codes: List[int] = None,
                 proxies: Dict[str, str] = None,
                 headers: Dict[str, str] = None,
                 before_request: Callable = None,
                 after_response: Callable = None):
        """
        初始化HTTP客户端
        
        Args:
            base_url: API基础URL
            timeout: 请求超时时间（秒）
            max_retries: 最大重试次数
            retry_delay: 重试间隔（秒）
            retry_status_codes: 需要重试的HTTP状态码列表
            proxies: 代理设置
            headers: 默认请求头
            before_request: 请求前拦截器函数
            after_response: 响应后拦截器函数
        """
        self.base_url = base_url.rstrip('/')
        self.timeout = timeout
        self.max_retries = max_retries
        self.retry_delay = retry_delay
        self.retry_status_codes = retry_status_codes or [429, 500, 502, 503, 504]
        self.proxies = proxies
        self.default_headers = headers or {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        }
        self.before_request = before_request
        self.after_response = after_response
        
        # 创建会话和配置重试策略
        self.session = self._create_session()
    
    def _create_session(self) -> requests.Session:
        """
        创建配置了重试策略的会话
        """
        session = requests.Session()
        
        # 配置重试策略
        retry_strategy = Retry(
            total=self.max_retries,
            backoff_factor=self.retry_delay,
            status_forcelist=self.retry_status_codes,
            allowed_methods=["GET", "POST", "PUT", "DELETE", "PATCH"]
        )
        
        # 添加HTTP适配器
        adapter = HTTPAdapter(max_retries=retry_strategy)
        session.mount("http://", adapter)
        session.mount("https://", adapter)
        
        # 设置默认请求头
        session.headers.update(self.default_headers)
        
        # 设置默认代理
        if self.proxies:
            session.proxies.update(self.proxies)
        
        return session
    
    def _prepare_request(self,
                         method: str,
                         url: str,
                         **kwargs) -> requests.Request:
        """
        准备请求对象
        
        Args:
            method: HTTP方法
            url: 请求URL
            **kwargs: 请求参数
            
        Returns:
            准备好的请求对象
        """
        # 如果是相对URL，则添加base_url
        if not url.startswith(('http://', 'https://')):
            url = f"{self.base_url}/{url.lstrip('/')}"
        
        # 合并请求头
        headers = kwargs.get('headers', {})
        merged_headers = {**self.default_headers, **headers}
        kwargs['headers'] = merged_headers
        
        # 为JSON数据添加Content-Type（如果未指定）
        if 'json' in kwargs and 'Content-Type' not in merged_headers:
            merged_headers['Content-Type'] = 'application/json'
        
        # 为文件上传移除Content-Type，让requests自动处理
        if 'files' in kwargs and 'Content-Type' in merged_headers:
            del merged_headers['Content-Type']
        
        # 创建请求对象
        request = requests.Request(method, url, **kwargs)
        
        # 应用请求前拦截器
        if self.before_request:
            request = self.before_request(request)
        
        return request
    
    def _process_response(self, 
                         response: requests.Response,
                         stream: bool = False) -> Union[Dict[str, Any], bytes, str]:
        """
        处理响应
        
        Args:
            response: 响应对象
            stream: 是否返回原始流
            
        Returns:
            处理后的响应数据
        """
        # 应用响应后拦截器
        if self.after_response:
            response = self.after_response(response)
        
        # 如果是流响应，直接返回
        if stream:
            return response.content
        
        # 根据Content-Type处理响应
        content_type = response.headers.get('Content-Type', '')
        
        if 'application/json' in content_type:
            try:
                return response.json()
            except ValueError:
                logger.warning("Response Content-Type is JSON but parsing failed")
        
        # 返回原始文本
        return response.text
    
    def request(self,
                method: str,
                url: str,
                params: Optional[Dict[str, Any]] = None,
                data: Optional[Union[Dict[str, Any], str]] = None,
                json_data: Optional[Dict[str, Any]] = None,
                headers: Optional[Dict[str, str]] = None,
                files: Optional[Dict[str, Any]] = None,
                timeout: Optional[int] = None,
                stream: bool = False,
                verify: bool = True,
                allow_redirects: bool = True,
                **kwargs) -> Union[Dict[str, Any], bytes, str]:
        """
        发送HTTP请求
        
        Args:
            method: HTTP方法
            url: 请求URL
            params: URL查询参数
            data: 表单数据
            json_data: JSON数据（注意这里用json_data而非json，避免与Python关键字冲突）
            headers: 请求头
            files: 文件上传
            timeout: 超时时间
            stream: 是否返回原始流
            verify: 是否验证SSL证书
            allow_redirects: 是否允许重定向
            **kwargs: 其他请求参数
            
        Returns:
            处理后的响应数据
            
        Raises:
            RequestError: 请求失败
        """
        # 构建请求参数
        request_kwargs = {
            'params': params,
            'data': data,
            'headers': headers,
            'files': files,
            'timeout': timeout or self.timeout,
            'verify': verify,
            'allow_redirects': allow_redirects,
            **kwargs
        }
        
        # 处理JSON数据（避免与Python关键字冲突）
        if json_data is not None:
            request_kwargs['json'] = json_data
        
        # 准备请求
        request = self._prepare_request(method, url, **request_kwargs)
        prepared_request = self.session.prepare_request(request)
        
        # 记录请求日志
        logger.debug(f"Request: {method} {prepared_request.url}")
        
        try:
            # 发送请求
            start_time = time.time()
            response = self.session.send(prepared_request)
            elapsed_time = time.time() - start_time
            
            # 记录响应日志
            logger.debug(f"Response: {response.status_code} ({elapsed_time:.3f}s)")
            
            # 检查状态码
            response.raise_for_status()
            
            # 处理响应数据
            return self._process_response(response, stream)
            
        except requests.RequestException as e:
            # 处理请求异常
            if isinstance(e, requests.HTTPError):
                status_code = e.response.status_code
                if status_code == 401:
                    raise AuthenticationError("认证失败", status_code, e.response)
                elif status_code == 429:
                    raise RateLimitError("请求频率过高", status_code, e.response)
                elif status_code == 400:
                    raise ValidationError("参数错误", status_code, e.response)
                else:
                    raise RequestError(f"请求失败: {str(e)}", status_code, e.response)
            else:
                raise RequestError(f"请求失败: {str(e)}")
    
    def get(self,
            url: str,
            params: Optional[Dict[str, Any]] = None,
            headers: Optional[Dict[str, str]] = None,
            timeout: Optional[int] = None,
            **kwargs) -> Union[Dict[str, Any], bytes, str]:
        """
        发送GET请求
        """
        return self.request(
            'GET', url, params=params, headers=headers, timeout=timeout, **kwargs
        )
    
    def post(self,
             url: str,
             data: Optional[Union[Dict[str, Any], str]] = None,
             json_data: Optional[Dict[str, Any]] = None,
             params: Optional[Dict[str, Any]] = None,
             headers: Optional[Dict[str, str]] = None,
             files: Optional[Dict[str, Any]] = None,
             timeout: Optional[int] = None,
             **kwargs) -> Union[Dict[str, Any], bytes, str]:
        """
        发送POST请求
        """
        return self.request(
            'POST',
            url,
            params=params,
            data=data,
            json_data=json_data,
            headers=headers,
            files=files,
            timeout=timeout,
            **kwargs
        )
    
    def put(self,
            url: str,
            data: Optional[Union[Dict[str, Any], str]] = None,
            json_data: Optional[Dict[str, Any]] = None,
            params: Optional[Dict[str, Any]] = None,
            headers: Optional[Dict[str, str]] = None,
            timeout: Optional[int] = None,
            **kwargs) -> Union[Dict[str, Any], bytes, str]:
        """
        发送PUT请求
        """
        return self.request(
            'PUT',
            url,
            params=params,
            data=data,
            json_data=json_data,
            headers=headers,
            timeout=timeout,
            **kwargs
        )
    
    def delete(self,
               url: str,
               params: Optional[Dict[str, Any]] = None,
               headers: Optional[Dict[str, str]] = None,
               timeout: Optional[int] = None,
               **kwargs) -> Union[Dict[str, Any], bytes, str]:
        """
        发送DELETE请求
        """
        return self.request(
            'DELETE', url, params=params, headers=headers, timeout=timeout, **kwargs
        )
    
    def patch(self,
              url: str,
              data: Optional[Union[Dict[str, Any], str]] = None,
              json_data: Optional[Dict[str, Any]] = None,
              params: Optional[Dict[str, Any]] = None,
              headers: Optional[Dict[str, str]] = None,
              timeout: Optional[int] = None,
              **kwargs) -> Union[Dict[str, Any], bytes, str]:
        """
        发送PATCH请求
        """
        return self.request(
            'PATCH',
            url,
            params=params,
            data=data,
            json_data=json_data,
            headers=headers,
            timeout=timeout,
            **kwargs
        )
    
    def close(self):
        """
        关闭会话
        """
        if hasattr(self, 'session'):
            self.session.close()
    
    def __enter__(self):
        """
        上下文管理器入口
        """
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        上下文管理器出口，自动关闭会话
        """
        self.close()


# 创建一个全局默认客户端实例
default_client = HttpClient()